package org.aergia.vinny.gui.view;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.print.PrinterException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import org.aergia.vinny.database.pojo.VinnyUser;
import org.aergia.vinny.gui.controller.VinnyController;
import org.aergia.vinny.gui.types.ColumnDefinition;

public abstract class DatabasePojoPanel extends JPanel implements DatabasePanel, ActionListener {

	protected abstract static class DatabasePojoTableModel<T extends Object> extends AbstractTableModel {

		protected static final int BOOLEAN_COLUMN_WIDTH = 50;
		protected static final int CARD_COLUMN_WIDTH = 90;
		protected static final int DATE_COLUMN_WIDTH = 150;
		protected static final int DURATION_COLUMN_WIDTH = 70;
		protected static final int NAME_COLUMN_WIDTH = 150;
		protected static final int WORKSTATION_COLUMN_WIDTH = 150;

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		protected VinnyController controller;
		protected List<T> records;
		private final List<ColumnDefinition> columnDefinitions;

		/**
		 * @param controller
		 */
		public DatabasePojoTableModel(final VinnyController controller, final List<ColumnDefinition> columnDefinitions) {
			this.controller = controller;
			this.columnDefinitions = columnDefinitions;
			this.records = new ArrayList<T>();
		}

		@Override
		public Class<?> getColumnClass(final int columnIndex) {
			return this.columnDefinitions.get(columnIndex).getColumnClass();
		}

		@Override
		public int getColumnCount() {
			return this.columnDefinitions.size();
		}

		@Override
		public String getColumnName(final int column) {
			return this.columnDefinitions.get(column).getColumnName();
		}

		public int getColumnSortKey(final int column) {
			return this.columnDefinitions.get(column).getColumnSortKey();
		}

		public SortOrder getColumnSortOrder(final int column) {
			return this.columnDefinitions.get(column).getColumnSortOrder();
		}

		public int getColumnWidth(final int column) {
			return this.columnDefinitions.get(column).getColumnWidth();
		}

		@Override
		public int getRowCount() {
			return this.records.size();
		}

		public void reload() {
			this.records.clear();
			this.populateTable();
			this.fireTableDataChanged();
		}

		protected void addNewRecord(final T volunteer) {
			this.records.add(volunteer);
		}

		protected T getRecord(final int selectedRow) {
			return this.records.get(selectedRow);
		}

		abstract protected void populateTable();

		protected void setRecord(final int selectedRow, final T record) {
			this.records.set(selectedRow, record);
		}
	}

	private static final long serialVersionUID = 1L;
	protected VinnyController controller;
	protected DatabasePojoTableModel<? extends Object> tableModel;
	protected VinnyUser user;
	private DatabaseActionPanel databaseAction;
	private JScrollPane jScrollPane0;

	private JTable table;

	/**
	 * @param controller
	 * @param tableModel
	 */
	public DatabasePojoPanel(final VinnyController controller, final VinnyUser user, final DatabasePojoTableModel<? extends Object> tableModel,
			final boolean includeOperations) {
		this(controller, user, tableModel, includeOperations, includeOperations, null);
	}

	/**
	 * @param controller
	 * @param tableModel
	 */
	public DatabasePojoPanel(final VinnyController controller, final VinnyUser user, final DatabasePojoTableModel<? extends Object> tableModel,
			final boolean includeOperations, final boolean includeModify) {
		this(controller, user, tableModel, includeOperations, includeModify, null);
	}

	/**
	 * @param controller
	 * @param tableModel
	 */
	public DatabasePojoPanel(final VinnyController controller, final VinnyUser user, final DatabasePojoTableModel<? extends Object> tableModel,
			final boolean includeOperations, final boolean includeModify, final JPanel additionalPanel) {
		this.controller = controller;
		this.user = user;
		this.tableModel = tableModel;
		this.initComponents(includeOperations, includeModify, additionalPanel);
	}

	@Override
	public void actionPerformed(final ActionEvent e) {
		if (e.getActionCommand().equals(DatabaseActionPanel.ACTION_ADD)) {
			this.addButtonActionPerformed(e);
		}
		if (e.getActionCommand().equals(DatabaseActionPanel.ACTION_DELETE)) {
			this.deleteButtonActionPerformed(e);
		}
		if (e.getActionCommand().equals(DatabaseActionPanel.ACTION_EDIT)) {
			this.editButtonActionPerformed(e);
		}
		if (e.getActionCommand().equals("Approve All")) {
			this.approveAllButtonActionPerformed(e);
		}
	}

	@Override
	public void printTable() throws PrinterException {
		this.table.print();
	}

	@Override
	public void refresh() {
		this.tableModel.reload();
		this.table.invalidate();
	}

	/**
	 * @param event
	 */
	abstract protected void addButtonActionPerformed(final ActionEvent event);

	/**
	 * @param event
	 */
	protected void approveAllButtonActionPerformed(final ActionEvent event) {
		// empty
	}

	/**
	 * @param event
	 */
	abstract protected void deleteButtonActionPerformed(final ActionEvent event);

	/**
	 * @param event
	 */
	abstract protected void editButtonActionPerformed(final ActionEvent event);

	protected int getSelectedRow() {
		return this.table.convertRowIndexToModel(this.table.getSelectedRow());
	}

	protected int[] getSelectedRows() {
		final int[] initialRows = this.table.getSelectedRows();
		if (initialRows.length == 0) {
			return initialRows;
		}
		final int[] rows = new int[initialRows.length];
		for (int i = 0; i < initialRows.length; i++) {
			rows[i] = this.table.convertRowIndexToModel(initialRows[i]);
		}
		return rows;
	}

	protected void setCellRenderer(final String identifier, final TableCellRenderer cellRenderer) {
		final TableColumn column = this.table.getColumn(identifier);
		column.setCellRenderer(cellRenderer);
	}

	protected void setColumnEditor(final String identifier, final TableCellEditor cellEditor) {
		final TableColumn column = this.table.getColumn(identifier);
		column.setCellEditor(cellEditor);
	}

	private JScrollPane getJScrollPane() {
		if (this.jScrollPane0 == null) {
			this.jScrollPane0 = new JScrollPane();
			this.jScrollPane0.setViewportView(this.getTable());
		}
		return this.jScrollPane0;
	}

	private JTable getTable() {
		if (this.table == null) {
			this.table = new JTable();
			this.table.setModel(this.tableModel);
			this.table.setAutoCreateRowSorter(true);
			final List<SortKey> sort = new ArrayList<SortKey>();
			TableColumn column = null;
			for (int i = 0; i < this.table.getColumnCount(); i++) {
				column = this.table.getColumnModel().getColumn(i);
				final int columnWidth = this.tableModel.getColumnWidth(i);
				final SortOrder order = this.tableModel.getColumnSortOrder(i);
				if (order != SortOrder.UNSORTED) {
					final int sortKey = this.tableModel.getColumnSortKey(i);
					sort.add(sortKey > sort.size() ? sort.size() : sortKey, new SortKey(i, order));
				}
				if (columnWidth != 0) {
					column.setMinWidth(columnWidth);
					column.setMaxWidth(columnWidth);
				}
			}
			if (sort.size() != 0) {
				this.table.getRowSorter().setSortKeys(sort);
			}
		}
		return this.table;
	}

	private void initComponents(final boolean includeOperations, final boolean includeModify, final JPanel additionalPanel) {
		this.setLayout(new BorderLayout());
		if (includeOperations || includeModify) {
			this.databaseAction = new DatabaseActionPanel(this, includeOperations, includeModify, additionalPanel);
			this.add(this.databaseAction, BorderLayout.NORTH);
		}
		this.add(this.getJScrollPane(), BorderLayout.CENTER);
		this.setSize(1024, 640);
	}

}
